<script setup lang="ts">
import { ref, computed } from 'vue';
import { UploadIcon, XIcon, FileIcon, ImageIcon, FileTextIcon, FileVideoIcon, FileAudioIcon, FileArchiveIcon } from 'lucide-vue-next';

import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Badge } from '@/components/ui/badge';
// import { http } from '@/utils/request';
import type { UploadResult } from './types';

// 文件项接口
interface FileItem {
  file: File;
  // 0: 等待中, 1: 上传中, 2: 成功, 3: 失败
  status: 0 | 1 | 2 | 3;
  progress: {
    uploaded: number;
    total: number;
  };
  src?: string;
  error?: string;
}

// 基础组件属性
interface BaseProps {
  accept?: string;
  multiple?: boolean;
  maxFileSize?: number;
  uploadUrl?: string;
  uploadParams?: Record<string, any>;
  uploadHeaders?: Record<string, string>;
  maxFiles?: number;
  autoUpload?: boolean;
  showFileList?: boolean;
  showProgress?: boolean;
  showDropZone?: boolean;
}

const props = withDefaults(defineProps<BaseProps>(), {
  accept: '',
  multiple: false,
  uploadUrl: '/file/upload',
  uploadParams: () => ({}),
  uploadHeaders: () => ({}),
  maxFiles: 10,
  autoUpload: true,
  showFileList: true,
  showProgress: true,
  showDropZone: true,
});

// 事件
const emit = defineEmits<{
  (e: 'fileChange', value: File[]): void;
  (e: 'fileRemove', value: File): void;
  (e: 'uploadSuccess', value: UploadResult): void;
  (e: 'uploadError', value: string, file: File): void;
  (e: 'uploadProgress', value: number, file: File): void;
}>();

// 状态管理
const isDragging = ref(false);
const errorMessage = ref('');
const fileList = ref<FileItem[]>([]);
const abortControllers = ref<Map<number, AbortController>>(new Map());

// 计算属性
const uploading = computed(() => fileList.value.some(item => item.status === 1));
const successCount = computed(() => fileList.value.filter(item => item.status === 2).length);
const failCount = computed(() => fileList.value.filter(item => item.status === 3).length);

const progress = computed(() => {
  const total = fileList.value.reduce((sum, item) => sum + item.progress.total, 0);
  const uploaded = fileList.value.reduce((sum, item) => sum + item.progress.uploaded, 0);
  return { total, uploaded };
});

const totalProgressPercent = computed(() => {
  if (progress.value.total === 0) {
    return 0;
  }
  return Math.round((progress.value.uploaded / progress.value.total) * 100);
});

// 格式化接受的文件类型显示
const acceptFormatted = computed(() => {
  if (!props.accept) {
    return '所有文件';
  }
  return props.accept
    .split(',')
    .map(type => type.replace(/\*/g, '所有').replace(/\./g, ''))
    .join(', ');
});

// 处理拖放事件
function handleDrop(e: DragEvent) {
  isDragging.value = false;
  const files = e.dataTransfer?.files;
  if (files && files.length) {
    handleFiles(Array.from(files));
  }
}

// 处理文件选择
function handleFileSelect(e: Event) {
  const input = e.target as HTMLInputElement;
  const files = input.files;
  if (files && files.length) {
    handleFiles(Array.from(files));
    input.value = '';
  }
}

// 处理文件
async function handleFiles(files: File[]) {
  if (fileList.value.length + files.length > props.maxFiles) {
    errorMessage.value = `最多只能上传 ${props.maxFiles} 个文件`;
    return;
  }

  if (!props.multiple && files.length > 1) {
    errorMessage.value = '只允许上传一个文件';
    return;
  }

  const validFiles: File[] = [];
  for (const file of files) {
    if (validateFile(file)) {
      validFiles.push(file);
    }
  }

  if (validFiles.length > 0) {
    appendFiles(validFiles);
    emit('fileChange', fileList.value.map(item => item.file));

    if (props.autoUpload) {
      await startUpload();
    }
  }
}

// 添加文件到列表
function appendFiles(files: File[]) {
  const newItems: FileItem[] = files.map(file => ({
    file,
    status: 0,
    progress: {
      uploaded: 0,
      total: file.size,
    },
  }));
  fileList.value.push(...newItems);
}

// 验证文件
function validateFile(file: File): boolean {
  if (props.accept && !isFileAccepted(file, props.accept)) {
    errorMessage.value = `文件类型不支持: ${file.name}`;
    return false;
  }

  if (props.maxFileSize && file.size > props.maxFileSize) {
    errorMessage.value = `文件过大: ${file.name} (最大: ${formatFileSize(props.maxFileSize)})`;
    return false;
  }

  return true;
}

// 检查文件是否被接受
function isFileAccepted(file: File, accept: string): boolean {
  const acceptTypes = accept.split(',');
  const fileName = file.name.toLowerCase();

  for (const type of acceptTypes) {
    const t = type.trim();
    if (t.startsWith('.')) {
      if (fileName.endsWith(t.toLowerCase())) {
        return true;
      }
    } else if (t.includes('/')) {
      if (file.type.startsWith(`${t.split('/')[0]}/`) || t === '*/*') {
        return true;
      }
    }
  }
  return false;
}

// 开始上传
async function startUpload() {
  const pendingItems = fileList.value
    .map((item, index) => ({ item, index }))
    .filter(({ item }) => item.status === 0);

  for (const { index } of pendingItems) {
    await upload(index);
  }
}

// 上传单个文件
async function upload(index?: number) {
  const indexes = index !== undefined ? [index] : fileList.value.map((_, i) => i);

  for (const i of indexes) {
    const fileItem = fileList.value[i];
    if (!fileItem || (fileItem.status !== 0 && fileItem.status !== 3)) {
      continue;
    }

    fileItem.status = 1;
    fileItem.progress.uploaded = 0;
    fileItem.error = undefined;

    const formData = new FormData();
    formData.append('file', fileItem.file);

    Object.entries(props.uploadParams).forEach(([key, value]) => {
      formData.append(key, value);
    });

    try {
      const abortController = new AbortController();
      abortControllers.value.set(i, abortController);

      // 使用 XMLHttpRequest 以便监听上传进度
      const response = await uploadWithProgress(formData, i, abortController.signal);

      fileItem.status = 2;
      fileItem.src = response.url;
      fileItem.progress.uploaded = fileItem.progress.total;

      const result: UploadResult = {
        fileName: response.filename || response.fileName,
        originalName: response.originalName,
        url: response.url,
        size: response.size,
        mimeType: response.mimeType,
      };

      emit('uploadSuccess', result);
      abortControllers.value.delete(i);
    } catch (error: any) {
      fileItem.status = 3;
      fileItem.error = error.message || '上传失败';
      emit('uploadError', fileItem.error || '', fileItem.file);
      abortControllers.value.delete(i);
    }
  }
}

// 使用 XMLHttpRequest 上传文件并监听进度
function uploadWithProgress(formData: FormData, index: number, signal: AbortSignal): Promise<any> {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    // 监听上传进度
    xhr.upload.addEventListener('progress', e => {
      if (e.lengthComputable) {
        const fileItem = fileList.value[index];
        if (fileItem) {
          fileItem.progress.uploaded = e.loaded;
          fileItem.progress.total = e.total;
          emit('uploadProgress', Math.round((e.loaded / e.total) * 100), fileItem.file);
        }
      }
    });

    // 监听完成
    xhr.addEventListener('load', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          const response = JSON.parse(xhr.responseText);
          // 处理响应数据结构
          if (response.code === '00000') {
            resolve(response.data);
          } else {
            reject(new Error(response.message || '上传失败'));
          }
        } catch {
          reject(new Error('响应解析失败'));
        }
      } else {
        reject(new Error(`上传失败: ${xhr.statusText}`));
      }
    });

    // 监听错误
    xhr.addEventListener('error', () => {
      reject(new Error('网络错误'));
    });

    // 监听取消
    xhr.addEventListener('abort', () => {
      reject(new Error('上传已取消'));
    });

    // 处理 AbortSignal
    signal.addEventListener('abort', () => {
      xhr.abort();
    });

    // 发送请求
    xhr.open('POST', import.meta.env.VITE_API_BASE_URL + props.uploadUrl);

    // 添加 token
    const token = localStorage.getItem('access_token');
    if (token) {
      xhr.setRequestHeader('Authorization', `Bearer ${token}`);
    }

    // 添加自定义 headers
    Object.entries(props.uploadHeaders).forEach(([key, value]) => {
      xhr.setRequestHeader(key, value);
    });

    xhr.send(formData);
  });
}

// 移除文件
function removeFile(index: number) {
  const fileItem = fileList.value[index];
  if (fileItem) {
    // 如果正在上传，先取消
    if (fileItem.status === 1) {
      const controller = abortControllers.value.get(index);
      if (controller) {
        controller.abort();
        abortControllers.value.delete(index);
      }
    }
    emit('fileRemove', fileItem.file);
    fileList.value.splice(index, 1);
  }
}

// 重新上传文件
async function retryUpload(index: number) {
  const fileItem = fileList.value[index];
  if (fileItem && fileItem.status === 3) {
    fileItem.status = 0;
    await upload(index);
  }
}

// 取消所有上传
function abort() {
  abortControllers.value.forEach(controller => controller.abort());
  abortControllers.value.clear();
  fileList.value.forEach(item => {
    if (item.status === 1) {
      item.status = 3;
      item.error = '上传已取消';
    }
  });
}

// 获取文件图标
function getFileIcon(file: File) {
  const type = file.type;
  const name = file.name.toLowerCase();

  if (type.startsWith('image/')) {
    return ImageIcon;
  }
  if (type.startsWith('video/')) {
    return FileVideoIcon;
  }
  if (type.startsWith('audio/')) {
    return FileAudioIcon;
  }
  if (type.includes('pdf') || name.endsWith('.pdf')) {
    return FileTextIcon;
  }
  if (type.includes('zip') || type.includes('rar') || type.includes('tar')
    || name.endsWith('.zip') || name.endsWith('.rar') || name.endsWith('.tar')) {
    return FileArchiveIcon;
  }
  return FileIcon;
}

// 格式化文件大小显示
function formatFileSize(bytes: number): string {
  if (bytes === 0) {
    return '0 Bytes';
  }
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
}

// 清空所有文件
function clearFiles() {
  abort();
  fileList.value = [];
  errorMessage.value = '';
}

// 暴露方法
defineExpose({
  fileList,
  startUpload,
  clearFiles,
  upload,
  abort,
  handleFiles,
});
</script>

<template>
  <div class="space-y-4">
    <!-- 拖放区域 -->
    <div
      v-if="showDropZone"
      class="border-2 rounded-lg p-8 text-center transition-all"
      :class="[isDragging ? 'border-primary bg-primary/5' : 'border-input bg-background/50 hover:bg-background/80']"
      @dragover.prevent
      @dragenter.prevent
      @dragleave.prevent
      @drop.prevent="handleDrop"
    >
      <div v-if="!uploading" class="space-y-2">
        <UploadIcon class="mx-auto h-12 w-12 text-muted-foreground" />
        <div class="space-y-1">
          <p class="text-sm font-medium">
            拖放文件到此处，或
          </p>
          <label
            for="base-file-upload"
            class="inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2 text-sm"
          >
            选择文件
          </label>
          <input
            id="base-file-upload"
            type="file"
            class="hidden"
            :accept="accept"
            :multiple="multiple"
            @change="handleFileSelect"
          />
        </div>
        <p class="text-xs text-muted-foreground">
          支持的格式: {{ acceptFormatted }}
          <template v-if="maxFileSize">
            ，最大文件大小: {{ formatFileSize(maxFileSize) }}
          </template>
          <template v-if="maxFiles">
            ，最多 {{ maxFiles }} 个文件
          </template>
        </p>
      </div>

      <!-- 上传进度 -->
      <div v-if="uploading && showProgress" class="space-y-2">
        <Progress :value="totalProgressPercent" class="h-2" />
        <p class="text-sm text-muted-foreground">
          正在上传... ({{ totalProgressPercent }}%)
        </p>
        <div class="flex justify-center space-x-2 text-xs text-muted-foreground">
          <span>上传中: {{ fileList.filter(item => item.status === 1).length }}</span>
          <span>成功: {{ successCount }}</span>
          <span>失败: {{ failCount }}</span>
        </div>
      </div>
    </div>

    <!-- 文件列表 -->
    <div v-if="fileList.length && showFileList" class="space-y-3">
      <div class="flex items-center justify-between">
        <h3 class="text-sm font-medium">
          文件列表 ({{ fileList.length }}/{{ maxFiles }})
        </h3>
        <div class="flex space-x-2">
          <Button
            v-if="!autoUpload && fileList.filter(item => item.status === 0).length > 0"
            size="sm"
            @click="startUpload"
          >
            开始上传
          </Button>
          <Button variant="outline" size="sm" @click="clearFiles">
            清空
          </Button>
        </div>
      </div>

      <div class="space-y-2">
        <div
          v-for="(fileItem, index) of fileList"
          :key="index"
          class="flex items-center justify-between rounded-md border p-3"
        >
          <div class="flex items-center space-x-3 flex-1 min-w-0">
            <component :is="getFileIcon(fileItem.file)" class="h-8 w-8 text-muted-foreground flex-shrink-0" />
            <div class="flex-1 min-w-0">
              <div class="flex items-center space-x-2">
                <p class="text-sm font-medium truncate">
                  {{ fileItem.file.name }}
                </p>
                <Badge :variant="fileItem.status === 2 ? 'default' : fileItem.status === 3 ? 'destructive' : 'secondary'">
                  {{ fileItem.status === 0 ? '等待中' : fileItem.status === 1 ? '上传中' : fileItem.status === 2 ? '成功' : '失败' }}
                </Badge>
              </div>
              <p class="text-xs text-muted-foreground">
                {{ formatFileSize(fileItem.file.size) }}
                <template v-if="fileItem.status === 2">
                  • <a :href="fileItem.src" target="_blank" class="text-primary hover:underline">查看文件</a>
                </template>
                <template v-if="fileItem.status === 3 && fileItem.error">
                  • {{ fileItem.error }}
                </template>
              </p>
            </div>
          </div>

          <div class="flex items-center space-x-2 flex-shrink-0">
            <div v-if="fileItem.status === 1 && showProgress" class="w-20">
              <Progress
                :value="fileItem.progress.total > 0 ? Math.round(fileItem.progress.uploaded / fileItem.progress.total * 100) : 0"
                class="h-1"
              />
            </div>
            <Button
              v-if="fileItem.status === 3"
              variant="outline"
              size="sm"
              @click="retryUpload(index)"
            >
              重试
            </Button>
            <Button
              variant="ghost"
              size="sm"
              class="h-7 w-7 p-0"
              @click="removeFile(index)"
            >
              <XIcon class="h-4 w-4" />
            </Button>
          </div>
        </div>
      </div>
    </div>

    <!-- 错误信息 -->
    <p v-if="errorMessage" class="text-sm text-destructive">
      {{ errorMessage }}
    </p>
  </div>
</template>
